package com.portal.mq.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.rabbit.annotation.EnableRabbit;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * RabbitMQ配置类
 * 提供RabbitMQ的核心组件配置，包括：
 * 1. 连接工厂配置：主机、端口、用户名、密码等
 * 2. 消息模板配置：消息转换器、确认机制等
 * 3. 消息监听器配置：消息确认模式、预取数量等
 * 4. 管理组件配置：用于管理队列、交换机等
 */
@Slf4j
@Configuration
@EnableRabbit  // 启用RabbitMQ注解支持
public class RabbitMQConfiguration {

    /**
     * RabbitMQ服务器主机地址
     * 从配置文件中读取，支持本地开发和生产环境的配置分离
     */
    @Value("${spring.rabbitmq.host}")
    private String host;

    /**
     * RabbitMQ服务器端口
     * 默认为5672（非SSL）或5671（SSL）
     */
    @Value("${spring.rabbitmq.port}")
    private int port;

    /**
     * RabbitMQ登录用户名
     * 用于身份认证
     */
    @Value("${spring.rabbitmq.username}")
    private String username;

    /**
     * RabbitMQ登录密码
     * 用于身份认证
     */
    @Value("${spring.rabbitmq.password}")
    private String password;

    /**
     * 创建RabbitMQ管理组件
     * 用于管理队列、交换机、绑定关系等
     * 
     * @param rabbitTemplate RabbitMQ消息模板
     * @return RabbitAdmin实例
     */
    @Bean
    public RabbitAdmin rabbitAdmin(RabbitTemplate rabbitTemplate) {
        return new RabbitAdmin(rabbitTemplate.getConnectionFactory());
    }

    /**
     * 创建RabbitMQ连接工厂
     * 配置连接信息并启用发布确认和发布返回机制
     * 
     * @return ConnectionFactory RabbitMQ连接工厂实例
     */
    @Bean
    public ConnectionFactory connectionFactory() {
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
        // 设置基本连接信息
        connectionFactory.setHost(host);
        connectionFactory.setPort(port);
        connectionFactory.setUsername(username);
        connectionFactory.setPassword(password);
        
        // 启用发布确认机制
        // CORRELATED表示异步确认，提供回调接口
        connectionFactory.setPublisherConfirmType(CachingConnectionFactory.ConfirmType.CORRELATED);
        
        // 启用发布返回机制
        // 当消息无法路由到队列时，会触发返回回调
        connectionFactory.setPublisherReturns(true);
        
        return connectionFactory;
    }

    /**
     * 创建消息转换器
     * 使用Jackson2JsonMessageConverter实现JSON格式的消息转换
     * 
     * @return MessageConverter 消息转换器实例
     */
    @Bean
    public MessageConverter messageConverter() {
        return new Jackson2JsonMessageConverter();
    }

    /**
     * 创建RabbitTemplate消息模板
     * 配置消息转换器和消息确认机制
     * 
     * @param connectionFactory RabbitMQ连接工厂
     * @return RabbitTemplate 消息模板实例
     */
    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        // 设置消息转换器，支持JSON格式
        rabbitTemplate.setMessageConverter(messageConverter());
        
        // 开启强制消息投递（mandatory）
        // 如果为true，则消息路由失败时会返回给生产者
        rabbitTemplate.setMandatory(true);
        
        // 设置消息确认回调
        // 当消息到达broker时触发
        rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
            if (ack) {
                log.info("消息已被路由接收");
            } else {
                log.error("消息发送到路由失败: {}", cause);
            }
        });

        // 设置消息返回回调
        // 当消息无法路由到队列时触发
        rabbitTemplate.setReturnsCallback(returned -> {
            log.error("消息被路由退回: exchange={}, routingKey={}, replyCode={}, replyText={}",
                    returned.getExchange(), 
                    returned.getRoutingKey(),
                    returned.getReplyCode(), 
                    returned.getReplyText());
        });

        return rabbitTemplate;
    }
}